/*
* Purpose: linux epoll api interface. 
* Created: 2012-04-05 Daly 
*/


#ifdef HAVE_EPOLL

#include "xnt_base.h"
#include "xnt_event.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>

#include <sys/epoll.h>
#include <errno.h>

//global variables
//Warning: Should not be operated by multi-threads in this version. 
//      The event should be controlled by one thread
// The next version may be protected by mutex.

int ep_init(xnt_conf_t* cf);
int ep_shutdown();
int ep_event_add(xnt_event_t* ev, int fd, int ev_mask);
int ep_event_del(xnt_event_t* ev, int fd, int ev_mask);
int ep_process_event(xnt_msec_t t);


static struct xnt_event_driver_t _drv_kqueue = {
    "EPOLL",
	ep_init,
	ep_shutdown,
	ep_event_add,
	ep_event_del,
	ep_process_event,
};

//global driver define
xnt_event_driver_t *g_event_drv = &_drv_kqueue;


int _epollfd;
static int _max_events;
static int _nevents;
static struct epoll_event *_events = NULL;

int ep_init(xnt_conf_t* cf)
{
	int max_fd = cf->max_fd;
	_epollfd = epoll_create( 100 );
	if (_epollfd < 0) return XNT_RET_ERR;
	_max_events = max_fd * 2;
	_events = (epoll_event*)malloc(sizeof(epoll_event) * _max_events + 1);
	return XNT_RET_OK;
}

int ep_shutdown()
{
	free(_events);
	_events = NULL;
	return 0;
}

int ep_event_add(xnt_event_t* ev, int fd, int ev_mask)
{
	struct epoll_event ep;
	if (ev_mask == XNT_EV_READ) ep.events = EPOLLIN;
	else if (ev_mask == XNT_EV_WRITE) ep.events = EPOLLOUT;
	
	ev->mask = ev_mask;
	ev->fd = fd;
	ep.data.ptr = (void*)ev;
	if (epoll_ctl(_epollfd, EPOLL_CTL_ADD, fd, &ep) == -1) {
		return XNT_RET_ERR;
	}
	return XNT_RET_OK;
}

int ep_event_del(xnt_event_t* ev, int fd, int ev_mask)
{
	struct epoll_event ep;
	if (ev_mask == XNT_EV_READ) ep.events = EPOLLIN;
	else if (ev_mask == XNT_EV_WRITE) ep.events = EPOLLOUT;

	ev->mask = 0;
	ev->fd = -1;
	ep.data.fd = fd;
	if (epoll_ctl(_epollfd, EPOLL_CTL_DEL, fd, &ep) == -1) {
		return XNT_RET_ERR;
	}
	return XNT_RET_OK;
}

//param: 
//  t  --  timeout. (millisecond or XNT_TIMER_INFINITY)
int ep_process_event(xnt_msec_t t)
{
	int i;
	int tm = (int)t;
	xnt_event_t* ev;
	epoll_event* ep;

	_nevents = epoll_wait(_epollfd, _events, _max_events, tm);
	if (_nevents < 0) {
		if (errno == EINTR) return 0;
		return XNT_RET_ERR;
	}
	for (i=0; i<_nevents; i++) {
		ep = &_events[i];
		if (ep->events & EPOLLIN || ep->events & EPOLLOUT) {
				ev = (xnt_event_t*)ep->data.ptr;
				if (ev->handler) ev->handler(ev, ev->fd);
		}
	}

	return _nevents;
}

#endif  // XNT_USE_KQUEUE

